<!DOCTYPE html>
<html lang="en">
	<head>
	  <meta charset="UTF-8">
	  <meta name="viewport" content="width=device-width, initial-scale=1.0">
	  <meta http-equiv="X-UA-Compatible" content="ie=edge">
	  <title>Document</title>
	</head>
	<body>
		<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
		<script>
			// const foo = 'bar';
			// const baz = {foo};
			// console.log(baz);// {foo: "bar"}

			// const baz = {foo:foo}

			// function f(x,y){
			// 	return {x,y};
			// }
			// 等同于
			// function f(x,y){
			// 	return {x: x, y: y};
			// }
			// console.log(f(1,2))//{x: 1, y: 2}

			// const o = {
			// 	method(){
			// 		return 'Hello';
			// 	}
			// }
			// //等同於
			// const o = {
			// 	method:function(){
			// 		return 'Hello';
			// 	}
			// };

			// let birth = '2000/01/01';
			// const Person = {
			// 	name:'張三',
			// 	birth,//等同於birth:birth
			// 	hello(){console.log('我的名字是',this.name);}//等同於hello:function(){}
			// }

			// function getPoint(){
			// 	const x = 1;
			// 	const y = 10;
			// 	return {x,y};
			// }
			// console.log(getPoint());//{x: 1, y: 10}

			// let ms = {};
			// function getItem(key){
			// 	return key in ms ? ms[key] : null;
			// }
			// function setItem(key,value){
			// 	ms[key] = value;
			// }
			// function clear(){
			// 	ms = {};
			// }
			// module.exports = {getItem,setItem,clear};
			// //等同於
			// module.exports = {
			// 	getItem:getItem,
			// 	setItem:setItem,
			// 	clear:clear
			// }

			// const cart = {
			// 	_wheels:4,
			// 	get wheels(){
			// 		return this._wheels;
			// 	},
			// 	set wheels(value){
			// 		if(value < this._wheels){
			// 			throw new Error('數值太小了！');
			// 		}
			// 		this._wheels = value;
			// 	}
			// }

			// const obj = {
			// 	class(){}
			// };
			// //等同於
			// var obj = {
			// 	'class':function(){}
			// };
			

			// const obj = {
			// 	*m() {
			// 		yield 'Hello world';
			// 	}
			// };

			// obj.foo = true;
			// obj['a'+'bc'] = 123;

			// var obj = {
			// 	foo:true,
			// 	abc:123
			// };

			// let propKey = 'foo';
			// let obj= {
			// 	[propKey]:true,
			// 	['a'+'bc']:123
			// };

			// let lastWord = 'last word';
			// const a = {
			//   'first word': 'hello',
			//   [lastWord]: 'world'
			// };
			// console.log(a['first word']);//hello
			// console.log(a[lastWord]);//word
			// console.log(a['last world']);//undefined


			// let obj = {
			// 	['h'+'ello'](){
			// 		return 'hi';
			// 	}
			// };
			// console.log(obj.hello());//hi

			// 报错
			// const foo ='bar';
			// const bar = 'abc';
			// const baz = { [foo] };//Uncaught SyntaxError: Unexpected token }

			// const foo = 'bar';
			// const baz = { [foo]: 'abc'};
			// console.log(baz);//{bar: "abc"}

			// const keyA = {a:1};
			// const keyB = {b:2};
			// const myObject = {
			// 	[keyA]:'valueA',
			// 	[keyB]:'valueB'
			// };
			// console.log(myObject);//Object {[object Object]: "valueB"}

			// const person = {
			// 	sayName(){
			// 		console.log('hello!')
			// 	}
			// };
			// console.log(person.sayName.name);//sayName

			// const obj = {
			// 	get foo(){

			// 	},
			// 	set foo(x){

			// 	}
			// }
			// console.log(obj.foo.name);// TypeError: Cannot read property 'name' of undefined

			// const obj = {
			// 	get foo(){

			// 	},
			// 	set foo(x){

			// 	}
			// }
			// const descriptor = Object.getOwnPropertyDescriptor(obj, 'foo');
			// console.log(descriptor.get.name);//get foo
			// console.log(descriptor.set.name);//set foo

			// console.log((new Function()).name)//anonymous

			// var doSomething = function(){};
			// console.log(doSomething.bind().name)//bound doSomething

			// const key1 = Symbol('description');
			// const key2  =Symbol();
			// let obj = {
			// 	[key1](){},
			// 	[key2](){},
			// };
			// console.log(obj[key1].name);//[description]
			// console.log(obj[key2].name);// ""

			// let obj = {foo:123};
			// console.log(Object.getOwnPropertyDescriptor(obj,'foo'));//{value: 123, writable: true, enumerable: true, configurable: true}

			// console.log(Object.getOwnPropertyDescriptor(Object.prototype,'toString').enumerable);//false

			// console.log(Object.getOwnPropertyDescriptor([], 'length').enumerable);//false

			// console.log(Object.getOwnPropertyDescriptor(class {foo() {}}.prototype, 'foo').enumerable);//false

			// const proto ={
			// 	foo:'hello'
			// };
			// const obj ={
			// 	foo:'world',
			// 	find(){
			// 		return super.foo;
			// 	}			
			// }
			// Object.setPrototypeOf(obj,proto);
			// console.log(obj.foo)// "world"

			// let {x,y,...z} = {x:1,y:2,a:3,b:4};
			// console.log(x);//1
			// console.log(y);//2
			// console.log(z);//{a: 3, b: 4}


			// 解构赋值要求等号右边是一个对象，所以如果等号右边是undefined或null，就会报错，因为它们无法转为对象
			// let {...z} = null;//Uncaught SyntaxError: Identifier 'z' has already been declared
			// let {...z} = undefined;//Uncaught SyntaxError: Identifier 'z' has already been declared

			// 解构赋值必须是最后一个参数，否则会报错
			// let {...x,y,z} = someObject;//Uncaught SyntaxError: Rest element must be last element
			// let {x,...y,...z} = someObject;//Uncaught SyntaxError: Rest element must be last element

			// let obj = {a:{b:1}};
			// let {...x} = obj;
			// obj.a.b = 2;
			// console.log(x.a.b);//2

			// let o1 = {a:1};
			// let o2 = {b:2};
			// o2._proto_ = o1;
			// let {...o3} = o2;
			// console.log(o3);//{ b: 2 }
			// console.log(o3.a);//undefined

			// const o = Object.create({x:1,y:2});
			// o.z = 3;
			// let {x,...newObj} = o;
			// let {y,z} = newObj;
			// console.log(x);//1
			// console.log(y);//undefined
			// console.log(z);//3

			// let z = {a:3,b:4};
			// let n = {...z};
			// console.log(n);//{a: 3, b: 4}

			// let foo = { ...['a','b','c']};
			// console.log(foo);//{0: "a", 1: "b", 2: "c"}

			// console.log({...{},a:1});//{a: 1}

			// console.log({...1})//{}
			// console.log({...true});//{}
			// console.log({...undefined});//{}
			// console.log({...null});//{}

			// console.log({...'hello'})//{0: "h", 1: "e", 2: "l", 3: "l", 4: "o"}

			//只是拷贝了对象实例的属性
			// let aClone = {...a};
			//等同于
			// let aClone = Object.assign({},a);

			//克隆一个对象，还拷贝对象原型的属性
			// 写法一的__proto__属性在非浏览器的环境不一定部署，因此推荐使用写法二和写法三
			// 写法一
			// const clone1 = {
			// 	_proto_:Object.getPrototypeOf(obj),
			// 	...obj
			// };
			// 写法二
			// const clone2 = Object.assign(
			// 	Object.create(Object.getPrototypeOf(obj)),
			// 	obj
			// );
			// 写法三
			// const clone3 = Object.create(
			// 	Object.getPrototypeOf(obj),
			// 	Object.getOwnPropertyDescriptors(obj)
			// )

			// 扩展运算符可以用于合并两个对象
			// let ab = {...a,...b};
			// 等同于
			// let ab = Object.assign({},a,b);

			// 用户自定义的属性，放在扩展运算符后面，则扩展运算符内部的同名属性会被覆盖掉
			// let aWithOverrides = {...a, x:1, y:2 };
			// 等同于
			// let aWithOverrides = {...a, ...{x:1,y:2}};
			// 等同于
			// let x = 1,y = 2,aWithOverrides = {...a,x,y};
			// 等同于
			// let aWithOverrides = Object.assign({},a, {x:1,y:2});


			// let newVersion = {
			// 	...previousVersion,
			// 	name:'New Name'
			// };

			//把自定义属性放在扩展运算符前面，就变成了设置新对象的默认属性值
			// let aWithDefaults = {x:1,y:2,...a};
			// 等同于
			// let aWithDefaults = Object.assign({},{x:1,y:2},a);
			// 等同于
			// let aWithDefaults = Object.assign({x:1,y:2},a);

			// const obj = {
			// 	...(x>1?{a:1}:{}),
			// 	b:2,
			// }

			// 并不会抛出错误，因为 x 属性只是被定义，但没执行
			// let aWithXGetter = {
			// 	...a,
			// 	get x(){
			// 		throw new Error('not throw yet');
			// 	}
			// };

			
			// 会抛出错误，因为 x 属性被执行了
			// let runtimeError = {
			// 	...a,
			// 	...{
			// 		get x(){
			// 			throw new Error('throw now');
			// 		}
			// 	}
			// };








		</script>
	</body>
</html>